#include <assert.h>
#include <time.h>
#include <string.h>

#include <xmlsec/xmltree.h>
#include <xmlsec/xmlenc.h>
#include <xmlsec/xmldsig.h>

#include <ghttp.h>

#include "common.h"

char *makeTime(time_t time) {
  char *tf = alloca(50);
  
  memset(tf, 0, 30);
  strftime(tf, 29, "%Y-%m-%dT%H:%M:%S%z", gmtime(&time));
  memmove(tf + strlen(tf) - 1, tf + strlen(tf) - 2, 3);
  tf[strlen(tf) - 3] = ':';
/*   fprintf(stderr, "time(%ld): %s\n", time, tf); */
  return g_strdup(tf);
}

int sendTxn(xmlDocPtr doc) {
  unsigned char *buf;
  int len, offset = 0;
  ghttp_request *request;
  ghttp_status stat;
  xmlDocPtr response;
  xmlNodePtr p1, p2;

  xmlDocDumpFormatMemory(doc, &buf, &len, 0);
  assert(len > 2);
  if(buf[0] == '<' && buf[1] == '?') {
    unsigned char *start = memchr(buf + 1, '<', len - 1);
    assert(start);
    offset = start - buf;
  }

  fprintf(stderr, "len %d offset %d\n", len, offset);
  request = ghttp_request_new();
  assert(request);
  ghttp_set_uri(request, "http://mikolaj.cx/settlement/services/DrawdownService");
  ghttp_set_type(request, ghttp_type_post);
  ghttp_set_header(request, http_hdr_Connection, "close");
  ghttp_set_header(request, http_hdr_Content_Type, "text/xml");
  ghttp_set_header(request, "SOAPAction", "");
  ghttp_set_body(request, buf + offset, len - offset);
  ghttp_prepare(request);
  stat = ghttp_process(request);
  fprintf(stderr, "ghttp result %d\n", stat);
  if(stat != ghttp_done)
    return 1;
  // fwrite(ghttp_get_body(request), ghttp_get_body_len(request), 1, stdout);
  response = xmlParseMemory(ghttp_get_body(request), ghttp_get_body_len(request));
  p1 = xmlDocGetRootElement(response);
  if(!p1 || xmlStrcmp(xmlSecGetNodeNsHref(p1), ns_soap) || 
     xmlStrcmp(xmlSecNodeGetName(p1), "Envelope"))
    return 2;
  p2 = xmlSecFindChild(p1, "Body", ns_soap);
  if(!p2)
    return 3;
  p1 = xmlSecFindChild(p2, "balanceStatus", ns_dws);
  if(!p1) {
    fprintf(stderr, "Request:\n%s\nResponse:\n", buf);
    xmlDocDump(stderr, response);
    return 4;
  }
  //ghttp_request_destroy(request);
  return 0;
}

void sign(char *template, xmlNodePtr tosign) {
  xmlDocPtr sigdoc;
  xmlNodePtr signode;
  xmlSecDSigCtxPtr ds;

  sigdoc = xmlParseFile(template);
  assert(sigdoc);
  signode = xmlDocCopyNode(xmlDocGetRootElement(sigdoc), tosign->doc, 1);
  assert(signode);
  //  xmlFreeDoc(sigdoc);
  xmlAddChild(tosign, signode);
  yankNameSpaces(xmlDocGetRootElement(tosign->doc));
  ds = xmlSecDSigCtxCreate(NULL);
  assert(ds);
  ds->flags |= XMLSEC_DSIG_FLAGS_STORE_SIGNEDINFO_REFERENCES | XMLSEC_DSIG_FLAGS_STORE_SIGNATURE;
  ds->signKey = loadRootKey();
  assert(!xmlSecDSigCtxSign(ds, signode));
}


xmlDocPtr makeDrawdown(xmlNodePtr authority, struct remuneration *rem) {
  xmlDocPtr drawdown;
  xmlNodePtr root, vi;
  //  char tf[100];
  char *ptr;
  xmlNsPtr dws, dosh, xsi;

  drawdown = xmlNewDoc("1.0");
  root = xmlNewDocNode(drawdown, NULL, "drawdown", NULL);
  dws = xmlNewNs(root, "urn:doshwsdl", "dws");
  dosh = xmlNewNs(root, "urn:dosh", "dosh");
  xsi = xmlNewNs(root, "http://www.w3.org/2001/XMLSchema-instance", "xsi");
  xmlSetNs(root, dws);
  xmlDocSetRootElement(drawdown, root);
  xmlAddChild(root, xmlDocCopyNode(rem->amount, drawdown, 1));
  xmlAddChild(root, xmlDocCopyNode(authority, drawdown, 1));
  vi = xmlNewNode(dosh, "validityInterval");
  ptr = makeTime(time(NULL) - 60);
  xmlAddChild(vi, xmlNewDocNode(drawdown, dosh, "notBefore", ptr));
  //  g_free(ptr);
  ptr = makeTime(time(NULL) + 86400);
  xmlAddChild(vi, xmlNewDocNode(drawdown, dosh, "notAfter", ptr));
  //  g_free(ptr);
  xmlAddChild(root, vi);
  /*
  xmlAddChild(root, xmlNewDocNode(drawdown, dws, "nonce", "this-is-a-nonce"));
  nonce = xmlNewNode(dws, "nonce");
  */
  /*
  memset(tf, 0, sizeof(tf));
  time(&t);
  strftime(tf, 98, "%Y-%m-%dT%H:%M:%S%z", localtime(&t));
  memmove(tf + strlen(tf) - 1, tf + strlen(tf) - 2, 2);
  tf[strlen(tf) - 3] = ':';
  xmlAddChild(root, xmlNewDocNode(drawdown, dws, "time", tf));
  */
  /*
  snprintf(tf, 99, "%.6g", rem->rate);
  amount = xmlNewDocNode(drawdown, dosh, "amount", tf);
  xmlNewProp(amount, "currency", "AUD");
  xmlAddChild(root, amount);
  */
  sign(DATADIR "/signature-drawdown.xml", root);
  return drawdown;
}

xmlDocPtr makeSoapReq(xmlNodePtr dd) {
  xmlNodePtr soaproot, body;
  xmlDocPtr soapreq;
  xmlNsPtr nssoap;

/*   auth = xmlSecFindNode(root, "authority", "urn:dosh"); */
//  fprintf(stderr, "found authority\n");
  soapreq = xmlNewDoc("1.0");
  soaproot = xmlNewDocNode(soapreq, NULL, "Envelope", NULL);
  nssoap = xmlNewNs(soaproot, "http://schemas.xmlsoap.org/soap/envelope/", "soap");
  xmlSetNs(soaproot, nssoap);
  xmlDocSetRootElement(soapreq, soaproot);
  body = xmlNewNode(nssoap, "Body");
  xmlAddChild(soaproot, body);
  //xmlAddChild(body, xmlDocCopyNode(auth, soapreq, 1));
  xmlAddChild(body, xmlDocCopyNode(dd, soapreq, 1));
  //xmlDocDump(stdout, soapreq);
  return soapreq;
}  
